package mya_dc.master_server;


import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;

import mya_dc.master_server.database.ClientsHashTable;
import mya_dc.master_server.database.ServersHashTable;
import mya_dc.master_server.database.SlavesHashSet;
import mya_dc.master_server.threads.ClientsManager;
import mya_dc.master_server.threads.CompiServManager;
import mya_dc.master_server.threads.SaveDataThread;
import mya_dc.master_server.threads.SlavesManager;
import mya_dc.shared_classes.MYA_Authentication;

/**
 * a Thread manager for new slaves-threads
 *
 * @author      Adam Levi
 * <br>			MYA
 */
public class MasterServer
{
	public static final String DATA_FILE_NAME = "ms_data.bin";
	public static final String LOG_FILE_NAME = "\\logs\\ms_log.txt";
	
	protected ServerSocket 	m_ClientServerSocket;
	protected ServerSocket	m_CompServerSocket;
	protected ServerSocket 	m_SlavesServerSocket;
	
	public 	  MasterServerGUI m_MasterServerGUI;
	
	protected ClientsHashTable		m_ClientsArray;
	protected SlavesHashSet			m_SlavesArray;
	public    ServersHashTable 		m_CompilationServers;
	protected ClientsManager 		m_ClientsManagerThread;
	protected SlavesManager 		m_SlavesManagerThread;
	protected CompiServManager 		m_CompiServManagerThread; 
	private	  SaveDataThread		m_SaveDataThread;
	
	public static void main(String[] args)
	{
		try {
			new MasterServer();
		} catch (IOException e) {

		}
	}
	
	/**
	 * Constructor
	 */
	public MasterServer() throws IOException
	{
		//initialize all others	
		try { loadData(); }
		catch (Exception e1)
		{
			m_CompilationServers 	= new ServersHashTable(1, m_MasterServerGUI);
			m_ClientsArray 			= new ClientsHashTable();		
		}
		m_SlavesArray = new SlavesHashSet();
		
		try {
			m_MasterServerGUI = new MasterServerGUI(this);
			m_MasterServerGUI.setVisible(true);
		} catch (Exception e) {
			e.printStackTrace();
		}
		//init save thread
		m_SaveDataThread = new SaveDataThread(this);
		new Thread(m_SaveDataThread).start();
		
		m_CompilationServers.m_ParentGUI = m_MasterServerGUI;
	}
	
	/**
	 * terminates the server
	 */
	protected void terminate()
	{
		try { m_CompServerSocket.close(); } catch(Exception e) {}
		try { m_SlavesServerSocket.close(); } catch(Exception e) {}
		try { m_ClientServerSocket.close(); } catch(Exception e) {}
		saveData();
	}
	
	
	/**
	 * opens the server's Sockets
	 * 
	 * @param iClientsPort - The port for new clients
	 * @param iCompServsPort - The port for new compilation servers
	 * @param iSlavesPort - The port for new slaves
	 */
	protected void createSockets(
			int iClientsPort, 
			int iCompServsPort, 
			int iSlavesPort
				) throws IOException
	{
		m_ClientServerSocket = new ServerSocket(iClientsPort);
		m_CompServerSocket   = new ServerSocket(iCompServsPort);
		m_SlavesServerSocket = new ServerSocket(iSlavesPort);
	}
	
	/**
	 * create threads
	 */
	protected void createThreads()
	{
		//creating manager threads
		m_ClientsManagerThread	= new ClientsManager(
				m_MasterServerGUI,
				m_ClientsArray,
				m_CompilationServers, 
				m_ClientServerSocket
			);
		m_SlavesManagerThread = new SlavesManager(
				m_MasterServerGUI,
				m_SlavesArray, 
				m_SlavesServerSocket
			);
		m_CompiServManagerThread = new CompiServManager(
				m_SlavesArray, 
				m_ClientsArray,
				m_CompilationServers,  
				m_CompServerSocket
			);
	
	}
	
	protected synchronized void runThreads()
	{
		new Thread(m_ClientsManagerThread).start();
		new Thread(m_SlavesManagerThread).start();
		new Thread(m_CompiServManagerThread).start();

	}
	
	
	public synchronized void saveData(String Location) throws FileNotFoundException, IOException
	{
		ObjectOutputStream outStream = new ObjectOutputStream(new FileOutputStream(Location));
		outStream.writeObject(m_ClientsArray);
		outStream.writeObject(m_CompilationServers);//.getServerCounter());
		outStream.flush();
		outStream.close();
	}
	
	public void saveData()
	{
		try
		{
			saveData( System.getProperty("user.dir") + "\\" + DATA_FILE_NAME);
		}
		catch (FileNotFoundException e) {}
		catch (IOException e) {}
	}
	
	public synchronized void loadData(String Location) throws FileNotFoundException, IOException, ClassNotFoundException
	{
		ObjectInputStream inStream = new ObjectInputStream(new FileInputStream(Location));
		//loading to tmp variables
		try
		{
			ClientsHashTable tmpClientsHT = (ClientsHashTable)inStream.readObject();
			ServersHashTable tmpServersHT = (ServersHashTable)inStream.readObject();
			tmpServersHT.setGUIParent(m_MasterServerGUI);
			//if loaded successfully then now we can copy from the tmp variables:
			m_ClientsArray = tmpClientsHT;
			m_CompilationServers = tmpServersHT;
			m_CompilationServers.clearRegisteredServers();	
		}
		catch(FileNotFoundException e)
		{
			inStream.close();
			throw e;
		}
		catch(IOException e)
		{
			inStream.close();
			throw e;
		}
		catch(ClassNotFoundException e)
		{
			inStream.close();
			throw e;
		}
		inStream.close();
	}
	
	private void loadData() throws Exception
	{
		loadData(System.getProperty("user.dir") + "\\" + DATA_FILE_NAME);
	}
	protected boolean deleteUser(MYA_Authentication user)
	{
		if (m_ClientsArray.getServer(user) == null)
			return false;
		Long serverID = m_ClientsArray.remove(user);
		m_CompilationServers.unRegisterClient(serverID);
		m_MasterServerGUI.UpdateClientsTables();
		return true;
	}
	protected boolean deleteAllUsers()
	{
		m_ClientsArray.clear();
		m_MasterServerGUI.UpdateClientsTables();
		return true;
	}
	
	public void verifyDataBase()
	{
		m_ClientsArray.verifyDataBase(m_CompilationServers);
	}

	public boolean isServerConnected(Long ID) {
		
		return m_CompilationServers.isConnected(ID);
	}
}
